using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace Zsa.Emulation.Util
{
    public static class TraceLogger
    {
        #region Fields
        /// <summary>
        /// The debug sources.
        /// </summary>
        private static DebugTraceSource[] _Sources;

        /// <summary>
        /// Used to flush and dispose the loggers.
        /// </summary>
        private static LoggerInternal _LoggerInternal;

        /// <summary>
        /// The other source.
        /// </summary>
        private static DebugTraceSource _OtherSource;

        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes the <see cref="T:Logger"/> class.
        /// </summary>
        static TraceLogger()
        {
            // Need to put error handling
            string _LogName = "Logs\\nes." + DateTime.Now.ToString("ddMMyy.HHmmss.");
            Trace.Listeners.Add(new TextWriterTraceListener(_LogName + "trace.log"));
            Trace.AutoFlush = true;
            DebugTraceSource.DefaultMaxWrites = 25;
            
            // Get the sub items.
            TraceSubsystem[] _LogsSub = Enum.GetValues(typeof(TraceSubsystem)) as TraceSubsystem[];
            _Sources = new DebugTraceSource[_LogsSub.Length];
            for (int _TempIndex = 0; _TempIndex < _Sources.Length; _TempIndex++)
            {
                _Sources[_TempIndex] = CreateLogSource(_LogsSub[_TempIndex], _LogName);
            }
            // Find the other source.
            _OtherSource = Array.Find<DebugTraceSource>(
                _Sources,
                delegate(DebugTraceSource src )
                {
                    return src.SubSystem == TraceSubsystem.Other;
                }
            );
            GetSubsystem(TraceSubsystem.Ppu).MaxWrites = 50;
            GetSubsystem(TraceSubsystem.MainMemory).MaxWrites = 100;
            
            // Used to flush and clean up.
            _LoggerInternal = new LoggerInternal();
        }
        #endregion Constructor

        #region Methods

        /// <summary>
        /// Creates the log source.
        /// </summary>
        /// <param name="subsystem">The subsystem.</param>
        /// <returns></returns>
        [DebuggerHidden()]
        private static DebugTraceSource CreateLogSource(TraceSubsystem subsystem,
            string prefix)
            
        {
            DebugTraceSource _DeviceHost = new DebugTraceSource(subsystem,0);
            _DeviceHost.Listeners.Clear();
            _DeviceHost.Listeners.Add(
                new TextWriterTraceListener(prefix + subsystem + ".log")
            );
            return _DeviceHost;
        }


        /// <summary>
        /// Gets the subsystem.
        /// </summary>
        /// <param name="subsystem">The subsystem.</param>
        /// <returns></returns>
        [DebuggerHidden()]
        internal static DebugTraceSource GetSubsystem(TraceSubsystem subsystem)
        {
            for (int _TempIndex = 0; _TempIndex < _Sources.Length; _TempIndex++)
            {
                if (_Sources[_TempIndex].SubSystem == subsystem)
                {
                    return _Sources[_TempIndex];
                }
            }
            return _OtherSource;
        }

        /// <summary>
        /// Traces the information.
        /// </summary>
        /// <param name="message">The message.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void TraceInformation(string message)
        {
            Trace.TraceInformation(message);
        }

        /// <summary>
        /// Traces the information.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="param">The param.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void TraceInformation(string message, params object[] param)
        {
            Trace.TraceInformation(message,param);
        }

        [Conditional("DEBUG"), DebuggerHidden()]
        public static void WriteException(Exception excep, string message)
        {
            Trace.TraceError(message);
            Trace.Indent();
            Trace.Indent();

            Trace.WriteLine("Type of " + excep.GetType().ToString());
            Trace.WriteLine("");

            Trace.WriteLine("Error Message " + excep.Message);
            Trace.WriteLine("");

            Trace.WriteLine("Stack :");
            Trace.WriteLine("");
            Trace.Indent();
            Trace.Indent();
            Trace.WriteLine(excep.StackTrace);
            Trace.Unindent();
            Trace.Unindent();

            Trace.Unindent();
            Trace.Unindent();
        }


        /// <summary>
        /// Asserts the specified condition.
        /// </summary>
        /// <param name="condition">if set to <c>true</c> [condition].</param>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void Assert(bool condition, string message,string category)
        {
            Trace.Assert(condition, message, category);
        }

        /// <summary>
        /// Writes the line if.
        /// </summary>
        /// <param name="condition">if set to <c>true</c> [condition].</param>
        /// <param name="message">The message.</param>
        /// <param name="category">The category.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void WriteLineIf(bool condition, string message, string category)
        {
            Trace.WriteLineIf(condition, message, category);
        }


        /// <summary>
        /// Write fields
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="obj"></param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void WriteObjectFields(bool condition, object obj)
        {
        }





        /// <summary>
        /// Breaks if attached.
        /// </summary>
        [Conditional("DEBUG"), DebuggerHidden()]
        public static void BreakIfAttached()
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                FlushAll();
                System.Diagnostics.Debugger.Break();
            }
        }

        /// <summary>
        /// Traces the information.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="message">The message.</param>
        [Conditional("TRACE"),DebuggerHidden()]
        public static void TraceInformation(TraceSubsystem subsys,
            string message)
        {
            DebugTraceSource _Source = GetSubsystem(subsys);
            _Source.TraceInformation(message);
        }

        /// <summary>
        /// Traces the information.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="message">The message.</param>
        /// <param name="param">The param.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void TraceInformation(TraceSubsystem subsys,
            string message, params object[] param)
        {
            DebugTraceSource _Source = GetSubsystem(subsys);
            _Source.TraceInformation(message, param);
        }

        /// <summary>
        /// Traces the event.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="type">The type.</param>
        /// <param name="eventId">The event id.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void TraceEvent(TraceSubsystem subsys, TraceEventType type, int eventId)
        {
            DebugTraceSource _Source = GetSubsystem(subsys);
            _Source.TraceEvent(type, eventId);
        }

        /// <summary>
        /// Traces the event.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="type">The type.</param>
        /// <param name="eventId">The event id.</param>
        /// <param name="message">The message.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void TraceEvent(TraceSubsystem subsys, TraceEventType type, int eventId, string message)
        {
            DebugTraceSource _Source = GetSubsystem(subsys);
            _Source.TraceEvent(type, eventId, message);
        }

        /// <summary>
        /// Traces the event.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="type">The type.</param>
        /// <param name="eventId">The event id.</param>
        /// <param name="message">The message.</param>
        /// <param name="args">The args.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void TraceEvent(TraceSubsystem subsys, TraceEventType type, int eventId, string message, params object[] args)
        {
            DebugTraceSource _Source = GetSubsystem(subsys);
            _Source.TraceEvent(type, eventId, message, args);
        }

        /// <summary>
        /// Traces the data.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="type">The type.</param>
        /// <param name="id">The id.</param>
        /// <param name="data">The data.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void TraceData(TraceSubsystem subsys, TraceEventType type, int id, object data)
        {
            DebugTraceSource _Source = GetSubsystem(subsys);
            _Source.TraceData(type, id, data);
        }

        /// <summary>
        /// Traces the data.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="type">The type.</param>
        /// <param name="id">The id.</param>
        /// <param name="data">The data.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void TraceData(TraceSubsystem subsys, TraceEventType type, int id, object[] data)
        {
            DebugTraceSource _Source = GetSubsystem(subsys);
            _Source.TraceData(type, id, data);
        }

        /// <summary>
        /// Flushes the specified subsys.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void Flush(TraceSubsystem subsys)
        {
            if (_LoggerInternal.IsDiposed)
            {
                return;
            }
            DebugTraceSource _Source = GetSubsystem(subsys);
            _Source.Flush();
        }

        /// <summary>
        /// Flushes all.
        /// </summary>
        [Conditional("TRACE"), DebuggerHidden()]
        public static void FlushAll()
        {
            if (_LoggerInternal.IsDiposed)
            {
                return;
            }
            for (int _TempIndex = 0; _TempIndex < _Sources.Length; _TempIndex++)
            {
                try
                {
                    _Sources[_TempIndex].Flush();
                }
                finally
                {
                }
            }
        }


        #endregion Methods

        #region Nested Types
        /// <summary>
        /// Used to make sure the sources are flushed.
        /// </summary>
        private class LoggerInternal
        {

            /// <summary>
            /// True if is disposed.
            /// </summary>
            private bool _IsDisposed = false;

            /// <summary>
            /// Initializes a new instance of the <see cref="T:LoggerInternal"/> class.
            /// </summary>
            /// <param name="listn">The listn.</param>
            public LoggerInternal()
            {
            }

            /// <summary>
            /// Gets a value indicating whether this instance is diposed.
            /// </summary>
            /// <value>
            /// 	<c>true</c> if this instance is diposed; otherwise, <c>false</c>.
            /// </value>
            public bool IsDiposed
            {
                get
                {
                    return _IsDisposed;
                }
            }

            /// <summary>
            /// Releases unmanaged resources and performs other cleanup operations before the
            /// <see cref="T:Zsa.Emulation.Util.Logger.LoggerInternal"/> is reclaimed by garbage collection.
            /// </summary>
            ~LoggerInternal()
            {
                if (_IsDisposed)
                {
                    return;
                }
                _IsDisposed = true;
                try
                {
                    TraceLogger.FlushAll();
                }
                finally
                {
                    
                }
            }
        }
        #endregion Nested Types




    }
}
